using System;
using System.Collections;
using System.Data;
using System.Web.UI.WebControls;
using PHSRAG.Insight21.Common.Pages;
using PHSRAG.Insight21.Context;
using PHSRAG.Insight21.InsightWorkFlow;
using PHSRAG.Insight21.Policy;
using PHSRAG.Utility;
using WorkFlow = PHSRAG.Insight21.InsightWorkFlow;
using Utils = PHSRAG.Insight21.InsightUtilities;

namespace PHSRAG.Insight21.Humans
{
    /// <summary>
    /// This is the page where users take actions on their Workflow items. This page uses Skelta Workflow's activity control.
    /// This control has two parts in it, list in datagrid form and detail of the item. Detail section has custom document view,
    /// comments tetxt and Accept and Reject buttons. The look and feel of this cotnrol can be changed by changing Activity.xml which
    /// resides in WorkflowElement - Activities - Config folder (actual path Workflow.NET\WorkflowElements\eIRBWFApp\en-US\Activities\Config)
    /// in Skelta installation.
    /// </summary>
    public partial class WorkFlowErrorActivityDetail : BasePage
    {
        #region Constants

        private const string AnnunciatorTitle = "The following error(s) occurred...";
        private const string CurrentPageIndexKey = "CurrentPageIndexKey_8A273D0A-07C1-4cbe-8B82-8468435E0457";
        private const string features = "width=600,height=400,scrollbars=yes,resizable=yes,status=no";
        private const int ItemsPerPage = 20;
        private const string WorkFlowReportPage = "WorkFlowReport.aspx";
        private const string workflowErrorActivityPageKey = "workflowErrorActivityPageKey_7972B25F-1519-40f1-8E28-1B6FA011918D";

        #endregion

        #region Private Variables

        private ICachePolicy cachePolicy;
        private string moduleType = string.Empty;
        private Hashtable pageCache;
        private ICachePolicy pageCachePolicy;

        #endregion

        #region ColumnIndex Enum

        /// <summary>
        /// Provides indexes used in the datagrid for specific columns
        /// </summary>
        private enum ColumnIndex
        {
            /// <summary>Protocol ID column index (hidden)</summary>
            ProtocolID,
            /// <summary>Protocol Process ID column index (hidden)</summary>
            ProtocolProcessID,
            /// <summary>access level column index (hidden)</summary>
            ///AccessLevel,
            /// <summary>protocol # column index</summary>
            ProtocolNumber,
            /// <summary>protocol title column index</summary>
            ProtocolTitle,
            /// <summary>type column index</summary>
            Type,
            /// <summary>Workflowtype column index</summary>
            WorkflowType,
            /// <summary>Submitter's Name column index</summary>
            SubmitterName,
            /// <summary>pi's name column index</summary>
            PIName,
            /// <summary>Date assigned column index</summary>
            DateAssigned,
            /// <summary>Workflow History column index</summary>
            WorkflowHistory,
            /// <summary>Access level column index</summary>
            AccessLevel,
            /// <summary>Work Flow execi=utionId Column index</summary>
            WFExecutionId, // hidden
            /// <summary>Action column index</summary>
            Action
        }

        #endregion

        #region private properties		

        /// <summary>
        /// ReadOnly property to get HumansWorkFlow helper object. This object gets loaded if it is not cached. Otherwise it get loaded
        /// and gets cached. This object is used to load the grid on the page.
        /// </summary>
        private HumansWorkFlow HumansWorkflow
        {
            get
            {
                HumansWorkFlow humansWF = ResultsCache.GetObject(cachePolicy, HumansWorkFlow.CacheKey) as HumansWorkFlow;

                if (humansWF == null)
                {
                    humansWF = new HumansWorkFlow(cachePolicy);
                    UserContext userContext = cachePolicy.GetCachedObject(UserContext.CacheKey) as UserContext;
                    object[] args = {userContext.DomainUserId, ModuleType};
                    humansWF.LoadErrorWorkflows(DataAccess.ConnectionString, args);
                    ResultsCache.SetObject(cachePolicy, HumansWorkFlow.CacheKey, humansWF);
                }
                return humansWF;
            }
        }

        /// <summary>
        /// Get/Set the current page within the datagrid for display. This information persists across page loads.
        /// </summary>
        private int CurrentPageIndex
        {
            get
            {
                Hashtable h = pageCachePolicy.GetCachedObject(workflowErrorActivityPageKey) as Hashtable;
                return (h == null || h[CurrentPageIndexKey] == null) ? 0 : (int) h[CurrentPageIndexKey];
            }
            set
            {
                Hashtable h = pageCachePolicy.GetCachedObject(workflowErrorActivityPageKey) as Hashtable ?? new Hashtable();
                h[CurrentPageIndexKey] = value;
                workflowErrorActivityList.CurrentPageIndex = value;
                pageCachePolicy.Cache(workflowErrorActivityPageKey, h);
            }
        }

        /// <summary>
        /// ReadOnly property to get module type that is to be passed in as a parameter to the load method
        /// </summary>
        private string ModuleType
        {
            get
            {
                if (moduleType == string.Empty)
                {
                    moduleType = (adminSubNavigation.SelectedTab.ToLower() == "humans management") ? "H" : "A";
                }
                return moduleType;
            }
        }

        #endregion

        #region Events

        /// <summary>
        /// Loads the page, sets the breadcrumb. Sets logged in user id to Skelta WorkflowActivity control's UserId property.
        /// Also, it sets filter on the activity control to make sure only selected Protocol Process appears in the control.
        /// </summary>
        /// <param name="sender">Control which rasied the event.</param>
        /// <param name="e">Arguments associated with the event.</param>
        protected void OnPageLoad(object sender, EventArgs e)
        {
            adminHumansSubNavigation.Visible = (adminSubNavigation.SelectedTab.ToLower() == "humans management") ? true : false;
            adminAnimalsSubNavigation.Visible = !adminHumansSubNavigation.Visible;
            if (adminAnimalsSubNavigation.Visible)
            {
                adminAnimalsSubNavigation.SelectedLink = "Workflow Errors";
            }
            else
            {
                adminHumansSubNavigation.SelectedLink = "Workflow Errors";
            }

            pageCache = pageCachePolicy.GetCachedObject(workflowErrorActivityPageKey) as Hashtable ?? new Hashtable();

            breadCrumb.Add(
                "Workflow Errors",
                Settings.GetKeyValue("SubNavigation.Admin.Humans.WorkFlowErrorURL", ""),
                Settings.GetKeyValue("BreadCrumb.SummaryPageLevel", 1));

            UserContext userContext = (UserContext) cachePolicy.GetCachedObject(UserContext.CacheKey);


            if (!IsPostBack && postbackGuard.IsValid)
            {
                workflowErrorActivityList.PageSize = Settings.GetKeyValue("HumansDataGrid.ItemsPerPage", ItemsPerPage);
            }
            if (CurrentPageIndex == 0)
            {
                InitializePager();
            }
            LoadDataGrid();
            ManagePagination();
            ManageNavigationLinks();
        }

        /// <summary>
        /// Resets the subnavigation selected link before the breadCrumb performs a redirect.
        /// </summary>
        /// <param name="sender">Control which rasied the event.</param>
        /// <param name="e">Arguments associated with the event.</param>
        private void OnBreadCrumbNavigate(object sender, CommandEventArgs e)
        {
            try
            {
                adminSubNavigation.Reset();
            }
            catch (Exception ex)
            {
                DisplayExceptionReport(ex);
            }
        }

        /// <summary>
        /// Resets the subnavigation selected link before the header or the breadCrumb performs a redirect.
        /// </summary>
        /// <param name="sender">Control which rasied the event.</param>
        /// <param name="e">Arguments associated with the event.</param>
        private void OnNavigate(object sender, CommandEventArgs e)
        {
            try
            {
                adminSubNavigation.Reset();
                adminHumansSubNavigation.Reset();
                adminAnimalsSubNavigation.Reset();
                ResultsCache.PurgeObject(cachePolicy);
            }
            catch (Exception ex)
            {
                DisplayExceptionReport(ex);
            }
        }

        /// <summary>
        /// Occurs when the FirstPage hyperlink is clicked on the pager.
        /// The page's DataGrid is reloaded with the data belonging to the first page.
        /// </summary>
        /// <param name="sender">The object raising an event.</param>
        /// <param name="e">Argument associated with the event</param>
        private void OnFirstPage(object sender, CommandEventArgs e)
        {
            CurrentPageIndex = 0;
            ManagePagination();
            LoadDataGrid();
            ManageNavigationLinks();
        }

        /// <summary>
        /// Occurs when the LastPage hyperlink is clicked on the pager.
        /// The page's DataGrid is reloaded with the data belonging to the last page.
        /// </summary>
        /// <param name="sender">The object raising an event.</param>
        /// <param name="e">Argument associated with the event</param>
        private void OnLastPage(object sender, CommandEventArgs e)
        {
            CurrentPageIndex = topPager.PageCount - 1;
            ManagePagination();
            LoadDataGrid();
            ManageNavigationLinks();
        }

        /// <summary>
        /// Occurs when the NexttPage hyperlink is clicked on the pager.
        /// The page's DataGrid is reloaded with the data belonging to the next page.
        /// </summary>
        /// <param name="sender">The object raising an event.</param>
        /// <param name="e">Argument associated with the event</param>
        private void OnNextPage(object sender, CommandEventArgs e)
        {
            ++CurrentPageIndex;
            ManagePagination();
            LoadDataGrid();
            ManageNavigationLinks();
        }

        /// <summary>
        /// Occurs when the PreviousPage hyperlink is clicked on the pager.
        /// The page's DataGrid is reloaded with the data belonging to the previous page.
        /// </summary>
        /// <param name="sender">The object raising an event.</param>
        /// <param name="e">Argument associated with the event</param>
        private void OnPreviousPage(object sender, CommandEventArgs e)
        {
            --CurrentPageIndex;
            ManagePagination();
            LoadDataGrid();
            ManageNavigationLinks();
        }

        /// <summary>
        /// This event is raised when the page index property is changed on the pager control.
        /// The page's DataGrid is reloaded with the data belonging to page specified by the index.
        /// </summary>
        /// <param name="source">The object raising an event.</param>
        /// <param name="e">Argument associated with the event</param>
        private void OnPageIndexChanged(object source, DataGridPageChangedEventArgs e)
        {
            workflowErrorActivityList.CurrentPageIndex = e.NewPageIndex;
            ManagePagination();
            LoadDataGrid();
            ManageNavigationLinks();
        }

        /// <summary>
        /// During data binding of the grid, a tooltip with the full title is associated with the
        /// shortened version that appears on the screen.  For every record, the corresponding lock/unlock
        /// </summary>
        /// <param name="sender">The object raising an event.</param>
        /// <param name="e">Argument associated with the event</param>
        private void OnItemDataBound(object sender, DataGridItemEventArgs e)
        {
            try
            {
                if ((e.Item.ItemType == ListItemType.Item ||
                    e.Item.ItemType == ListItemType.AlternatingItem))
                {
                    e.Item.Cells[(int) ColumnIndex.ProtocolTitle].ToolTip = e.Item.Cells[(int) ColumnIndex.ProtocolTitle].Text;
                }

                if (((Label) e.Item.FindControl("protocolProcessId")).Text != "0")
                {
                    ImageButton reportImage = new ImageButton();
                    reportImage.ImageUrl = "~/Images/Note.gif";
                    reportImage.Attributes["onclick"] =
                        string.Format("window.open('{0}?PPId={1}&ExecId={2}', 'WorkflowReport', '{3}');return false;",
                            WorkFlowReportPage, (((Label) e.Item.FindControl("protocolProcessId")).Text),
                            e.Item.Cells[(int) ColumnIndex.WFExecutionId].Text, features);

                    e.Item.Cells[(int) ColumnIndex.WorkflowHistory].Controls.Add(reportImage);
                }

                LinkButton actionButton = (LinkButton) e.Item.Cells[(int) ColumnIndex.Action].Controls[0];
                actionButton.Attributes["onclick"] = "return confirm('Are you sure you want to clean the workflow for the protocol?');";
            }
            catch (Exception ex)
            {
                ExceptionReport.WriteToEventLog("Error occurred while trying to bind the data to the grid", ex, true, ExceptionReport.ReportType.Error);
                annunciator.Message = "Error occurred while trying to bind the data to the grid";
            }
        }

        /// <summary>
        /// Handler for event raised while each data grid item is created.
        /// If the item being created is the data grid header, do the following:
        /// Add the appropriate sort symbol to denote ascending/descending sequence for the current sort column.
        /// This requires us to locate the cell which corresponding to the column within the collection of TableCell
        /// maintained by the DataGridItem which is under creation. That collection is indexed numerically, the value being
        /// the position of the column within the columns collection maintained by the data grid.
        /// One simple way to do this is to create static mapping between the name of the column and its index. However, this
        /// suffers from the shortcoming that if the sequence of columns is changed on the page, the static binding MUST
        /// be changed too. If they get out of sync, we would be sorting on inappropriate columns!
        /// To avoid this, and to render the code independent of how the page gets managed, we will build a Hashtable of
        /// column indeces, keyed by the sort expression of each column. That way, we could do a quick lookup to get the
        /// index of a column and set the sort symbol on that column.
        /// If the item being created is a data row item, disable its view state.
        /// </summary>
        /// <param name="sender">Control which raised the event</param>
        /// <param name="e">Argument associated with the event</param>
        private void OnItemCreated(object sender, DataGridItemEventArgs e)
        {
            try
            {
                if (e.Item.ItemType == ListItemType.Header)
                {
                    HumansWorkFlow humanWorkflow = HumansWorkflow;
                    Label sortSymbol = new Label();
                    sortSymbol.Text = humanWorkflow.SortAscending ? InsightUtilities.Constants.SortAscendingText : InsightUtilities.Constants.SortDescendingText;
                    sortSymbol.Font.Name = InsightUtilities.Constants.SortSymbolFontName;

                    Hashtable columnMap = new Hashtable();
                    foreach (DataGridColumn column in workflowErrorActivityList.Columns)
                    {
                        columnMap[column.SortExpression] = workflowErrorActivityList.Columns.IndexOf(column);
                    }

                    string sortColumn = humanWorkflow.SortColumn;
                    if (columnMap.ContainsKey(sortColumn))
                    {
                        (e.Item.Cells[(int) columnMap[sortColumn]]).Controls.Add(sortSymbol);
                    }
                }
                else if ((e.Item.ItemType == ListItemType.Item) || (e.Item.ItemType == ListItemType.AlternatingItem))
                {
                    e.Item.EnableViewState = false;
                }
            }
            catch (Exception ex)
            {
                ExceptionReport.WriteToEventLog("Error occurred while trying to load the control to the grid", ex, true, ExceptionReport.ReportType.Error);
                annunciator.Message = "Error occurred while trying to load the control";
            }
        }


        private void OnWorkflowErrorItemCommand(object source, DataGridCommandEventArgs e)
        {
            try
            {
                if (!postbackGuard.IsValid)
                {
                    return;
                }

                if (e.CommandName == "CleanWorkflow")
                {
                    HumansWorkFlow.ClearWorkflow(Int32.Parse(e.Item.Cells[(int) ColumnIndex.WFExecutionId].Text));
                    cachePolicy.UnCache(HumansWorkFlow.CacheKey);
                    LoadDataGrid();
                    ManagePagination();
                    ManageNavigationLinks();
                }
            }
            catch (Exception ex)
            {
                DisplayExceptionReport(ex);
            }
        }

        #endregion

        #region Web Form Designer generated code

        protected override void OnInit(EventArgs e)
        {
            //
            // CODEGEN: This call is required by the ASP.NET Web Form Designer.
            //
            InitializeComponent();
            base.OnInit(e);
            cachePolicy = (Policy.ICachePolicy) Session[Policy.Names.CachePolicyKey];
            pageCachePolicy = (Policy.ICachePolicy) Session[Policy.Names.PageCachePolicyKey];
        }

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.Load += new System.EventHandler(this.OnPageLoad);
            this.workflowErrorActivityList.ItemDataBound += new System.Web.UI.WebControls.DataGridItemEventHandler(this.OnItemDataBound);
            this.workflowErrorActivityList.ItemCommand += new System.Web.UI.WebControls.DataGridCommandEventHandler(this.OnWorkflowErrorItemCommand);
            this.topPager.FirstPage += new System.Web.UI.WebControls.CommandEventHandler(this.OnFirstPage);
            this.topPager.PreviousPage += new System.Web.UI.WebControls.CommandEventHandler(this.OnPreviousPage);
            this.topPager.NextPage += new System.Web.UI.WebControls.CommandEventHandler(this.OnNextPage);
            this.topPager.LastPage += new System.Web.UI.WebControls.CommandEventHandler(this.OnLastPage);
            this.bottomPager.FirstPage += new System.Web.UI.WebControls.CommandEventHandler(this.OnFirstPage);
            this.bottomPager.PreviousPage += new System.Web.UI.WebControls.CommandEventHandler(this.OnPreviousPage);
            this.bottomPager.NextPage += new System.Web.UI.WebControls.CommandEventHandler(this.OnNextPage);
            this.bottomPager.LastPage += new System.Web.UI.WebControls.CommandEventHandler(this.OnLastPage);
            this.header.Navigate += new System.Web.UI.WebControls.CommandEventHandler(this.OnNavigate);
            this.breadCrumb.Navigate += new System.Web.UI.WebControls.CommandEventHandler(this.OnNavigate);
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Displays an error message on the screen to the user with the exception, and all
        /// inner exception messages.
        /// </summary>
        /// <param name="ex">The exception to report on</param>
        private void DisplayExceptionReport(Exception ex)
        {
            annunciator.Title = AnnunciatorTitle;
            annunciator.Message = ExceptionReport.GetExceptionInfo(ex, false);
        }

        /// <summary>
        /// Resets the hash table that stores page data, and also the cachedProtocolId, 
        /// which indicate which protocol the cached page data is based on
        /// </summary>
        private void UnCachePageData()
        {
            pageCache = new Hashtable();
            pageCachePolicy.Cache(workflowErrorActivityPageKey, pageCache);
        }

        /// <summary>
        /// Initilize the first record, last record and current page values for the pager controls.
        /// Set the current page index to 0 and manage navigation links.
        /// </summary>
        private void InitializePager()
        {
            bottomPager.FirstRecord = topPager.FirstRecord = 1;
            bottomPager.LastRecord = topPager.LastRecord =
                Math.Min(workflowErrorActivityList.PageSize, workflowErrorActivityList.Items.Count);
            bottomPager.CurrentPage = topPager.CurrentPage = 1;
            CurrentPageIndex = 0;
            ManageNavigationLinks();
        }

        /// <summary>
        /// Pagination management
        /// </summary>
        private void ManagePagination()
        {
            int currentPageIndex = CurrentPageIndex;
            workflowErrorActivityList.CurrentPageIndex = currentPageIndex;

            int startIndex = currentPageIndex*workflowErrorActivityList.PageSize;
            bottomPager.FirstRecord = topPager.FirstRecord = startIndex + 1;

            int lastRecord = HumansWorkflow.ActivityCount;
            bottomPager.LastRecord = topPager.LastRecord =
                Math.Min(startIndex + workflowErrorActivityList.PageSize, lastRecord);
        }

        /// <summary>
        /// Enables or Disables links to other pages within the grid
        /// </summary>
        private void ManageNavigationLinks()
        {
            bottomPager.EnableFirstPage = topPager.EnableFirstPage =
                bottomPager.EnablePreviousPage = topPager.EnablePreviousPage =
                    (workflowErrorActivityList.CurrentPageIndex != 0);

            bottomPager.EnableLastPage = topPager.EnableLastPage =
                bottomPager.EnableNextPage = topPager.EnableNextPage =
                    (workflowErrorActivityList.CurrentPageIndex != (topPager.PageCount - 1));
        }

        /// <summary>
        /// This method gets called from page load as well as from pagination events, sorting and searching controls.
        /// This method binds data to the grid and also take care of pagination calculation.
        /// </summary>        
        private void LoadDataGrid()
        {
            HumansWorkFlow humansWF = HumansWorkflow;

            workflowErrorActivityList.VirtualItemCount = bottomPager.RecordCount = topPager.RecordCount =
                humansWF.ActivityCount;
            int currentPageIndex = CurrentPageIndex;
            int pageCount = (topPager.RecordCount/workflowErrorActivityList.PageSize) +
                (((topPager.RecordCount%workflowErrorActivityList.PageSize) == 0) ? 0 : 1);
            bottomPager.PageCount = topPager.PageCount = pageCount;
            bottomPager.CurrentPage = topPager.CurrentPage = currentPageIndex + 1;
            DataTable wfActivityTable = humansWF.GetErroredWorkflowItems(
                currentPageIndex*workflowErrorActivityList.PageSize, workflowErrorActivityList.PageSize);

            /*DataTable wfActivityTable = humansWF.GetWorkflowActivityList (
                currentPageIndex * workflowErrorActivityList.PageSize, workflowErrorActivityList.PageSize);*/
            workflowErrorActivityList.DataSource = wfActivityTable;
            workflowErrorActivityList.DataBind();

            if (wfActivityTable.Rows.Count == 0)
            {
                gridContainer.Visible = false;
                annunciator.Message = "The search criteria yielded no results.";
            }
            else
            {
                gridContainer.Visible = true;
                annunciator.Message = string.Empty;
                recordCount.Text = topPager.RecordCount.ToString();
            }
        }

        #endregion
    }
}